/*
 * Copyright (C) 2017 Fundación Inria Chile
 * Copyright (C) 2019 HAW Hamburg
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @defgroup    net_gnrc_lorawan GNRC LoRaWAN
 * @ingroup     net_gnrc
 * @brief       GNRC LoRaWAN stack implementation
 *
 * @{
 *
 * @file
 * @brief   GNRC LoRaWAN API definition
 *
 * @author  José Ignacio Alamos <jose.alamos@haw-hamburg.de>
 * @author  Francisco Molina <femolina@uc.cl>
 */
#ifndef NET_GNRC_LORAWAN_H
#define NET_GNRC_LORAWAN_H

#include "gnrc_lorawan_internal.h"
#include "assert.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @defgroup net_gnrc_lorawan_conf GNRC LoRaWAN compile configurations
 * @ingroup net_gnrc_conf
 * @{
 */
/**
 * @brief the minimum symbols to detect a LoRa preamble
 */
#ifndef CONFIG_GNRC_LORAWAN_MIN_SYMBOLS_TIMEOUT
#define CONFIG_GNRC_LORAWAN_MIN_SYMBOLS_TIMEOUT 30
#endif
/** @} */

#define GNRC_LORAWAN_REQ_STATUS_SUCCESS (0)     /**< MLME or MCPS request successful status */
#define GNRC_LORAWAN_REQ_STATUS_DEFERRED (1)    /**< the MLME or MCPS confirm message is asynchronous */

/**
 * @brief MCPS events
 */
typedef enum {
    MCPS_EVENT_RX,              /**< MCPS RX event */
    MCPS_EVENT_NO_RX,           /**< MCPS no RX event */
} mcps_event_t;

/**
 * @brief LoRaWAN activation mechanism
 */
typedef enum {
    MLME_ACTIVATION_NONE,       /**< MAC layer is not activated */
    MLME_ACTIVATION_ABP,        /**< MAC layer activated by ABP */
    MLME_ACTIVATION_OTAA        /**< MAC layer activated by OTAA */
} mlme_activation_t;

/**
 * @brief MAC Information Base attributes
 */
typedef enum {
    MIB_ACTIVATION_METHOD,      /**< type is activation method */
    MIB_DEV_ADDR,               /**< type is dev addr */
    MIB_RX2_DR,                 /**< type is rx2 DR */
} mlme_mib_type_t;

/**
 * @brief MLME primitive types
 */
typedef enum {
    MLME_JOIN,                  /**< join a LoRaWAN network */
    MLME_LINK_CHECK,            /**< perform a Link Check */
    MLME_RESET,                 /**< reset the MAC layer */
    MLME_SET,                   /**< set the MIB */
    MLME_GET,                   /**< get the MIB */
    MLME_SCHEDULE_UPLINK        /**< schedule uplink indication */
} mlme_type_t;

/**
 * @brief MCPS primitive types
 */
typedef enum {
    MCPS_CONFIRMED,             /**< confirmed data */
    MCPS_UNCONFIRMED            /**< unconfirmed data */
} mcps_type_t;

/**
 * @brief MAC Information Base descriptor for MLME Request-Confirm
 */
typedef struct {
    mlme_mib_type_t type;               /**< MIB attribute identifier */
    union {
        mlme_activation_t activation;   /**< holds activation mechanism */
        void *dev_addr;                 /**< pointer to the dev_addr */
        uint8_t rx2_dr;                 /** datarate of second rx window */
    };
} mlme_mib_t;

/**
 * @brief MAC (sub) Layer Management Entity (MLME) request representation
 */
typedef struct {
    union {
        mlme_lorawan_join_t join;   /**< Join Data holder */
        mlme_mib_t mib;             /**< MIB holder */
    };
    mlme_type_t type;               /**< type of the MLME request */
} mlme_request_t;

/**
 * @brief Mac Common Part Sublayer (MCPS) request representation
 */
typedef struct {
    union {
        mcps_data_t data;   /**< MCPS data holder */
    };
    mcps_type_t type;       /**< type of the MCPS request */
} mcps_request_t;

/**
 * @brief MAC (sub) Layer Management Entity (MLME) confirm representation
 */
typedef struct {
    int16_t status;                         /**< status of the MLME confirm */
    mlme_type_t type;                       /**< type of the MLME confirm */
    union {
        mlme_link_req_confirm_t link_req;   /**< Link Check confirmation data */
        mlme_mib_t mib;                     /**< MIB confirmation data */
    };
} mlme_confirm_t;

/**
 * @brief Mac Common Part Sublayer (MCPS) confirm representation
 */
typedef struct {
    int16_t status;     /**< status of the MCPS confirm */
    mcps_type_t type;   /**< type of the MCPS confirm */
    iolist_t *msdu;     /**< pointer to the msdu */
} mcps_confirm_t;

/**
 * @brief Mac Common Part Sublayer (MCPS) indication representation
 */
typedef struct {
    mcps_type_t type;       /**< type of the MCPS indication */
    union {
        mcps_data_t data;   /**< MCPS Data holder */
    };
} mcps_indication_t;

/**
 * @brief MAC (sub) Layer Management Entity (MLME) indication representation
 */
typedef struct {
    mlme_type_t type; /**< type of the MLME indication */
} mlme_indication_t;

/**
 * @brief Indicate the MAC layer there was a timeout event
 *
 * @param[in] mac pointer to the MAC descriptor
 */
void gnrc_lorawan_radio_rx_timeout_cb(gnrc_lorawan_t *mac);

/**
 * @brief Indicate the MAC layer when the transmission finished
 *
 * @param[in] mac pointer to the MAC descriptor
 */
void gnrc_lorawan_radio_tx_done_cb(gnrc_lorawan_t *mac);

/**
 * @brief Indicate the MAC layer reception of a frame went wrong.
 *
 * @param[in] mac pointer to the MAC descriptor
 */
static inline void gnrc_lorawan_radio_rx_error_cb(gnrc_lorawan_t *mac)
{
    /* The failed reception is seen by the MAC layer as an RX timeout */
    gnrc_lorawan_radio_rx_timeout_cb(mac);
}

/**
 * @brief Indicate the MAC layer that the timer was fired
 *
 * @param[in] mac pointer to the MAC descriptor
 */
void gnrc_lorawan_timeout_cb(gnrc_lorawan_t *mac);

/**
 * @brief Init GNRC LoRaWAN
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] joineui pointer to Join EUI
 * @param[in] ctx pointer to LoRaWAN context
 */
void gnrc_lorawan_init(gnrc_lorawan_t *mac, uint8_t *joineui, const gnrc_lorawan_key_ctx_t *ctx);

/**
 * @brief Perform a MLME request
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] mlme_request the MLME request
 * @param[out] mlme_confirm the MLME confirm. `mlme_confirm->status` could either
 *             be GNRC_LORAWAN_REQ_STATUS_SUCCESS if the request was OK,
 *             GNRC_LORAWAN_REQ_STATUS_DEFERRED if the confirmation is deferred
 *             or an standard error number
 */
void gnrc_lorawan_mlme_request(gnrc_lorawan_t *mac,
                               const mlme_request_t *mlme_request,
                               mlme_confirm_t *mlme_confirm);

/**
 * @brief Perform a MCPS request
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] mcps_request the MCPS request
 * @param[out] mcps_confirm the MCPS confirm. `mlme_confirm->status` could either
 *             be GNRC_LORAWAN_REQ_STATUS_SUCCESS if the request was OK,
 *             GNRC_LORAWAN_REQ_STATUS_DEFERRED if the confirmation is deferred
 *             or an standard error number
 */
void gnrc_lorawan_mcps_request(gnrc_lorawan_t *mac,
                               const mcps_request_t *mcps_request,
                               mcps_confirm_t *mcps_confirm);

/**
 * @brief Fetch a LoRaWAN packet from the radio.
 *
 *        To be called on radio RX done event.
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] data pointer to the psdu. Must not be NULL. Use
 *            @ref gnrc_lorawan_radio_rx_error_cb instead if the reception was
 *            not successful.
 * @param[in] size size of the PSDU
 */
void gnrc_lorawan_radio_rx_done_cb(gnrc_lorawan_t *mac, uint8_t *data,
                                   size_t size);

/**
 * @brief MCPS indication callback
 * @note Supposed to be implemented by the user of GNRC LoRaWAN
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] ind pointer of the indication (see @ref mcps_indication_t)
 */
void gnrc_lorawan_mcps_indication(gnrc_lorawan_t *mac, mcps_indication_t *ind);

/**
 * @brief MLME indication callback
 * @note Supposed to be implemented by the user of GNRC LoRaWAN
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] ind pointer of the indication (see @ref mlme_indication_t)
 */
void gnrc_lorawan_mlme_indication(gnrc_lorawan_t *mac, mlme_indication_t *ind);

/**
 * @brief MCPS Confirm callback
 * @note Supposed to be implemented by the user of GNRC LoRaWAN
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] confirm pointer to the confirm (see @ref mcps_confirm_t)
 */
void gnrc_lorawan_mcps_confirm(gnrc_lorawan_t *mac, mcps_confirm_t *confirm);

/**
 * @brief MLME confirm callback
 * @note Supposed to be implemented by the user of GNRC LoRaWAN
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] confirm pointer to the confirm (see @ref mlme_confirm_t)
 */
void gnrc_lorawan_mlme_confirm(gnrc_lorawan_t *mac, mlme_confirm_t *confirm);

/**
 * @brief Get netdev pointer from mac descriptor
 * @note Supposed to be implemented by the user of GNRC LoRaWAN
 *
 * @param[in] mac pointer to the MAC descriptor
 *
 * @return pointer to the @ref netdev_t structure
 */
netdev_t *gnrc_lorawan_get_netdev(gnrc_lorawan_t *mac);

/**
 * @brief Set the channel mask in order to enable or disable LoRaWAN channels
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] channel_mask the channel mask. LSB maps to channel 0
 *
 * @return 0 on success
 * @return -EINVAL if @p channel_mask is zero or if the channel mask tries to
 *         enable an undefined channel
 */
int gnrc_lorawan_phy_set_channel_mask(gnrc_lorawan_t *mac, uint16_t channel_mask);

/**
 * @brief Set a timer with the given time
 * @note Supposed to be implemented by the user of GNRC LoRaWAN
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param us timeout microseconds
 */
void gnrc_lorawan_set_timer(gnrc_lorawan_t *mac, uint32_t us);

/**
 * @brief Remove the current timer
 * @note Supposed to be implemented by the user of GNRC LoRaWAN
 *
 * @param[in] mac pointer to the MAC descriptor
 */
void gnrc_lorawan_remove_timer(gnrc_lorawan_t *mac);

/**
 * @brief Set unconfirmed uplink redundancy
 *
 * @pre   @p redundancy <= 14
 *
 * @param[in] mac pointer to the MAC descriptor
 * @param[in] redundancy number of unconfirmed uplink retransmissions
 */
static inline void gnrc_lorawan_set_uncnf_redundancy(gnrc_lorawan_t *mac,
                                                     uint8_t redundancy)
{
    assert(redundancy <= (0xF - 1));
    mac->mcps.redundancy = redundancy;
}

#ifdef __cplusplus
}
#endif

#endif /* NET_GNRC_LORAWAN_H */
/** @} */
